package com.wyl.niupi.framework.tools;


import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.beust.jcommander.internal.Lists;
import com.wyl.niupi.framework.tools.helper.FieldForGeneratorMysql;
import com.wyl.niupi.framework.util._BeanUtil;
import freemarker.template.Configuration;
import freemarker.template.Template;

import javax.sql.DataSource;
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
import java.sql.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 代码生成器，使用步骤如下所示
 * 第一步，配置基本信息
 * 第二步，运行main方法
 *
 * @author 吴宇亮 on 2019/06/27
 */
public class CodeGeneratorForMysql {

    //------------------- 第一步，配置基本信息
    //代码要生成到哪个目录下
    private static final String basicGeneratorPath = "E:\\ideaWorkspace\\niupi_springboot\\code\\src\\main\\java\\com\\wyl\\niupi\\yxc";
    // 数据库
    private static final String driverClassName = "com.mysql.cj.jdbc.Driver";
    private static final String url = "jdbc:mysql://localhost:3306/yxc?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC";
    private static final String username = "root";
    private static final String password = "123456";
    // 默认作者
    private static final String authorDefaultValue = "无名氏";
    // 默认模块名
    private static final String moduleDefaultValue = "yxc";


    //------------------- 第二步，运行main方法
    public static void main(String[] args) throws Exception {
        ask();
        generatorEntity(true);
        generatorController();
        generatorService();
        generatorServiceImpl();
        generatorDao();
        generatorDaoXml();

        System.out.println(tableName + "的代码已经生成完毕");
    }

    /**
     * 询问，初始化参数
     */
    private static void ask() {
        author = scanner("作者", authorDefaultValue);
        moduleName = scanner("模块名", moduleDefaultValue);
        tableName = scanner("表名", "");
        javabeanName = _BeanUtil.transform_2UpperCase(_BeanUtil.transform_2UpperCase(tableName));
        nowDateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm"));

        //String isNeedImportAndExport = scanner("是否需要生成导入和导出", "false");
        //TODO，检查提示是否要覆盖生成

        //获取模板所在的目录
        templateFoldPath = basicGeneratorPath.substring(0, basicGeneratorPath.indexOf("\\src\\main") + "\\src\\main".length()) + "\\resources\\templates";
        //根据javaBean所在的路径获取基本的包名
        int startIndex = basicGeneratorPath.indexOf("src\\main\\java\\");
        int lastIndexOf = basicGeneratorPath.lastIndexOf("\\");
        basicPackageName = basicGeneratorPath.substring(startIndex + "src\\main\\java\\".length(), lastIndexOf).replaceAll("\\\\", ".");
        //获取javabean的类名
        String _javabeanName = _BeanUtil.transform_2UpperCase(tableName);
        javabeanName = _javabeanName.substring(0, 1).toUpperCase() + _javabeanName.substring(1);
        //javaBean的变量名
        javabeanVar = javabeanName.substring(0, 1).toLowerCase() + javabeanName.substring(1);
    }

    /**
     * <p>
     * 读取控制台内容
     * </p>
     */
    private static String scanner(String tip, String defaultValue) {
        Scanner scanner = new Scanner(System.in);
        String help = StrUtil.isBlank(defaultValue) ? "请输入" + tip + "：" : "请输入" + tip + "(" + defaultValue + ")：";
        System.out.println(help);
        if (scanner.hasNextLine()) {
            String value = scanner.nextLine();
            if (StrUtil.isNotBlank(value)) {
                return value;
            }else if(StrUtil.isNotBlank(defaultValue)){
                return defaultValue;
            }
        }
        throw new IllegalArgumentException("请输入正确的" + tip + "！");
    }

    private static void generatorController() throws Exception {
        //如果文件已经存在，则不进行生成。
        String generatorPath = basicGeneratorPath + "\\controller";
        FileUtil.mkdir(new File(generatorPath));
        File file = new File(generatorPath + "\\" + javabeanName + "Controller.java");
        if(file.exists()){
            System.out.println("controller已经存在，不进行生成，以免覆盖");
            System.out.println("------------------------------");
            return;
        }

        // 第一步：实例化Freemarker的配置类, 配置ftl文件所在的路径
        Configuration conf = new Configuration();
        conf.setDirectoryForTemplateLoading(new File(templateFoldPath ));

        // 第二步：设置ftl模板的路径 、初始化数据源、最后文件要输出的位置
        Template template = conf.getTemplate("controller.java.ftl");						//设置ftl模板的路径
        Map<String, Object> generatorMap = new HashMap<>(); 		//定义数据源
        Writer out = new FileWriter(file); //定义最终文件生成位置

        // 第三步：往数据源里添加数据，将数据嵌入模板中，生成文件
        // 作者、时间、注释
        generatorMap.put("author", author);
        generatorMap.put("date", nowDateTime);
        generatorMap.put("tableComment", tableComment);
        //包名
        generatorMap.put("package", "package " + basicPackageName + "." + moduleName + ".controller;");
        //类名
        generatorMap.put("javabeanName", javabeanName);
        generatorMap.put("javabeanVar", javabeanVar);
        //字段列表
        generatorMap.put("fieldForGeneratorList", fieldList);
        //导入service
        generatorMap.put("importServicePath", basicPackageName + ".service." + javabeanName +"Service;");
        //拦截路径
        generatorMap.put("requestMapping", "/" + moduleName + "/" + javabeanVar);
        //controller类名
        generatorMap.put("controllerName", javabeanName + "Controller");
        //service名
        generatorMap.put("serviceName", javabeanName + "Service");
        // 执行生成
        template.process(generatorMap, out);
        out.flush();
        out.close();

        System.out.println(tableName + "的controller已经生成完毕");
        System.out.println("------------------------------");
    }

    /**
     * @param isForceGenerator 是否强制生成，强制生成则有可能覆盖文件
     */
    private static void generatorEntity(boolean isForceGenerator) throws Exception {
        //读取数据源，获得表的字段信息
        fieldList = getColumnList();

        //如果文件已经存在，则不进行生成。
        String generatorPath = basicGeneratorPath + "\\entity";
        FileUtil.mkdir(new File(generatorPath));
        File file = new File(generatorPath + "\\" + javabeanName + ".java");
        if(!isForceGenerator){
            if(file.exists()){
                System.out.println("javaBean已经存在，不进行生成，以免覆盖");
                System.out.println("------------------------------");
                return;
            }
        }

        // 第一步：实例化Freemarker的配置类, 配置ftl文件所在的路径
        Configuration conf = new Configuration();
        conf.setDirectoryForTemplateLoading(new File(templateFoldPath ));

        // 第二步：设置ftl模板的路径 、初始化数据源、最后文件要输出的位置
        Template template = conf.getTemplate("entity.java.ftl");						//设置ftl模板的路径
        Map<String, Object> generatorMap = new HashMap<>(); 		//定义数据源
        Writer out = new FileWriter(file); //定义最终文件生成位置

        // 第三步：往数据源里添加数据，将数据嵌入模板中，生成文件
        generatorMap.put("package", "package " + basicPackageName + "." + moduleName + ".entity;");
        // 作者、时间
        generatorMap.put("author", author);
        generatorMap.put("date", nowDateTime);
        //类名
        generatorMap.put("javabeanName", javabeanName);
        //表注释
        generatorMap.put("tableComment", tableComment);
        //字段列表
        generatorMap.put("fieldForGeneratorList", fieldList);
        // 执行生成
        template.process(generatorMap, out);
        out.flush();
        out.close();

        System.out.println(tableName + "的entity已经生成完毕");
        System.out.println("------------------------------");
    }

    public static void generatorService() throws Exception {
        //如果文件已经存在，则不进行生成。
        String generatorPath = basicGeneratorPath + "\\service";
        FileUtil.mkdir(new File(generatorPath));
        File file = new File(generatorPath + "\\" + javabeanName + "Service" + ".java");
        if(file.exists()){
            System.out.println("service已经存在，不进行生成，以免覆盖");
            System.out.println("------------------------------");
            return;
        }

        // 第一步：实例化Freemarker的配置类, 配置ftl文件所在的路径
        Configuration conf = new Configuration();
        conf.setDirectoryForTemplateLoading(new File(templateFoldPath));

        // 第二步：设置ftl模板的路径 、初始化数据源、最后文件要输出的位置
        Template template = conf.getTemplate("service.java.ftl");						//设置ftl模板的路径
        Map<String, Object> generatorMap = new HashMap<>(); 		//定义数据源
        Writer out = new FileWriter(file); //定义最终文件生成位置

        // 第三步：往数据源里添加数据，将数据嵌入模板中，生成文件
        // 作者、时间、注释
        generatorMap.put("author", author);
        generatorMap.put("date", nowDateTime);
        generatorMap.put("tableComment", tableComment);
        //类名
        generatorMap.put("javabeanName", javabeanName);
        //字段列表
        generatorMap.put("fieldForGeneratorList", fieldList);
        //包名
        generatorMap.put("package", "package " + basicPackageName + "." + moduleName + ".service;");
        generatorMap.put("importList", Arrays.asList("import " + basicPackageName + "." + moduleName + ".entity." + javabeanName + ";"));
        generatorMap.put("javabeanName", javabeanName);
        generatorMap.put("interfaceName", javabeanName + "Service");

        // 执行生成
        template.process(generatorMap, out);
        out.flush();
        out.close();

        System.out.println(tableName + "的Service已经生成完毕");
        System.out.println("------------------------------");
    }

    private static void generatorServiceImpl() throws Exception {
        //如果文件已经存在，则不进行生成。
        String generatorPath = basicGeneratorPath + "\\service\\impl";
        FileUtil.mkdir(new File(generatorPath));
        File file = new File(generatorPath + "\\" + javabeanName + "ServiceImpl" + ".java");
        if(file.exists()){
            System.out.println("serviceImpl已经存在，不进行生成，以免覆盖");
            System.out.println("------------------------------");
            return;
        }

        // 第一步：实例化Freemarker的配置类, 配置ftl文件所在的路径
        Configuration conf = new Configuration();
        conf.setDirectoryForTemplateLoading(new File(templateFoldPath ));

        // 第二步：设置ftl模板的路径 、初始化数据源、最后文件要输出的位置
        Template template = conf.getTemplate("serviceImpl.java.ftl");						//设置ftl模板的路径
        Map<String, Object> generatorMap = new HashMap<>(); 		//定义数据源
        Writer out = new FileWriter(file); //定义最终文件生成位置

        // 第三步：往数据源里添加数据，将数据嵌入模板中，生成文件
        // 作者、时间、注释
        generatorMap.put("author", author);
        generatorMap.put("date", nowDateTime);
        generatorMap.put("tableComment", tableComment);
        //类名
        generatorMap.put("javabeanName", javabeanName);
        generatorMap.put("javabeanVar", javabeanVar);

        generatorMap.put("package", "package " + basicPackageName + "." + moduleName + ".service.impl;");
        generatorMap.put("importList", Lists.newArrayList(
                "import " + basicPackageName + "." + moduleName + ".entity." + javabeanName + ";"
                , "import " + basicPackageName + "." + moduleName + ".dao." + javabeanName + "Dao;"
                , "import " + basicPackageName + "." + moduleName + ".service." + javabeanName + "Service;"
        ));
        generatorMap.put("javabeanName", javabeanName);
        generatorMap.put("javabeanVar", javabeanVar);
        generatorMap.put("className", javabeanName + "ServiceImpl");
        generatorMap.put("interfaceName", javabeanName + "Service");
        generatorMap.put("daoClassName", javabeanName + "Dao");

        // 执行生成
        template.process(generatorMap, out);
        out.flush();
        out.close();

        System.out.println(javabeanName + "的ServiceImpl已经生成完毕");
        System.out.println("------------------------------");
    }

    public static void generatorDao() throws Exception {
        //如果文件已经存在，则不进行生成。
        String generatorPath = basicGeneratorPath + "\\dao";
        FileUtil.mkdir(new File(generatorPath));
        File file = new File(generatorPath + "\\" + javabeanName + "Dao" + ".java");
        if(file.exists()){
            System.out.println("dao已经存在，不进行生成，以免覆盖");
            System.out.println("------------------------------");
            return;
        }

        // 第一步：实例化Freemarker的配置类, 配置ftl文件所在的路径
        Configuration conf = new Configuration();
        conf.setDirectoryForTemplateLoading(new File(templateFoldPath));

        // 第二步：设置ftl模板的路径 、初始化数据源、最后文件要输出的位置
        Template template = conf.getTemplate("dao.java.ftl");						//设置ftl模板的路径
        Map<String, Object> generatorMap = new HashMap<>(); 		//定义数据源
        Writer out = new FileWriter(file); //定义最终文件生成位置

        // 第三步：往数据源里添加数据，将数据嵌入模板中，生成文件
        generatorMap.put("javabeanName", javabeanName);
        generatorMap.put("package", "package " + basicPackageName + "." + moduleName + ".dao;");
        generatorMap.put("interfaceName", javabeanName + "Dao");
        generatorMap.put("importList", Lists.newArrayList(
                "import " + basicPackageName + "." + moduleName + ".entity." + javabeanName + ";"
        ));

        // 执行生成
        template.process(generatorMap, out);
        out.flush();
        out.close();

        System.out.println(javabeanName + "的dao已经生成完毕");
        System.out.println("------------------------------");
    }

    private static void generatorDaoXml()throws Exception {
        //如果文件已经存在，则不进行生成。
        String generatorPath = basicGeneratorPath + "\\dao";
        FileUtil.mkdir(new File(generatorPath));
        File file = new File(generatorPath + "\\" + javabeanName + "Dao" + ".xml");
        if(file.exists()){
            System.out.println("daoXml已经存在，不进行生成，以免覆盖");
            System.out.println("------------------------------");
            return;
        }

        // 第一步：实例化Freemarker的配置类, 配置ftl文件所在的路径
        Configuration conf = new Configuration();
        conf.setDirectoryForTemplateLoading(new File(templateFoldPath));

        // 第二步：设置ftl模板的路径 、初始化数据源、最后文件要输出的位置
        Template template = conf.getTemplate("dao.xml.ftl");						//设置ftl模板的路径
        Map<String, Object> generatorMap = new HashMap<>(); 		//定义数据源
        Writer out = new FileWriter(file); //定义最终文件生成位置

        // 第三步：往数据源里添加数据，将数据嵌入模板中，生成文件
        generatorMap.put("interfaceQualifiedName", basicPackageName + "." + moduleName + ".dao." + javabeanName + "Dao");

        // 执行生成
        template.process(generatorMap, out);
        out.flush();
        out.close();

        System.out.println(javabeanName + "的daoXml已经生成完毕");
        System.out.println("------------------------------");
    }

    /**
     * 读取数据源，获得表的字段信息List<FieldForGeneratorMysql>
     * @return 返回tableName的字段信息
     */
    private static List<FieldForGeneratorMysql> getColumnList() throws Exception {
        Map<String, String> setting = new HashMap<>();
        setting.put("driverClassName", driverClassName);
        setting.put("url", url);
        setting.put("username", username);
        setting.put("password", password);
        DataSource dataSource = DruidDataSourceFactory.createDataSource(setting);
        Connection connection = dataSource.getConnection();

        Statement statement = connection.createStatement();
        ResultSet resultSet = statement.executeQuery("select table_schema, table_name, table_comment from information_schema.tables where table_schema = '" + moduleName + "' and table_name = '" + tableName + "';");
        while (resultSet.next()) {
            tableComment = resultSet.getString("table_comment");
        }
        resultSet.close();
        statement.close();

        //查询表字段的注释
        PreparedStatement preparedStatement = connection.prepareStatement(
                "select column_name, column_comment from information_schema.columns where table_schema = '" + moduleName + "' and table_name = '" + tableName + "'");
        resultSet = preparedStatement.executeQuery();
        Map<String, String> fieldCommentMap = new HashMap<>();
        while (resultSet.next()) {
            String column_name = resultSet.getString("column_name");
            String column_comment = resultSet.getString("column_comment");
            fieldCommentMap.put(column_name, column_comment);
        }
        resultSet.close();
        preparedStatement.close();

        //查询表中有什么字段
        preparedStatement = connection.prepareStatement("desc " + tableName);
        resultSet = preparedStatement.executeQuery();
        List<FieldForGeneratorMysql> list = new ArrayList<>();
        while (resultSet.next()) {
            FieldForGeneratorMysql vo = new FieldForGeneratorMysql();
            vo.setColumnName(resultSet.getString("Field"));
            vo.setColumnType(resultSet.getString("Type"));
            vo.setIsNullable(resultSet.getString("Null"));
            vo.setColumnKey(resultSet.getString("Key"));
            vo.setColumnDefault(resultSet.getString("Default"));
            vo.setExtra(resultSet.getString("Extra"));
            vo.setComment(fieldCommentMap.get(vo.getColumnName()));
            list.add(vo);
        }
        resultSet.close();
        preparedStatement.close();

        for (FieldForGeneratorMysql column : list) {
            column.setColumnRealName(_BeanUtil.transform_2UpperCase(column.getColumnName()));
            column.setUppercaseName(column.getColumnName().toUpperCase());
            if(column.getColumnType().startsWith(FieldForGeneratorMysql.VARCHAR) || column.getColumnType().startsWith(FieldForGeneratorMysql.CHAR)){
                column.setColumnRealType("String");
            }else if(column.getColumnType().startsWith(FieldForGeneratorMysql.INT)){
                column.setColumnRealType("Integer");
            }else if(column.getColumnType().startsWith(FieldForGeneratorMysql.DECIMAL)){
                column.setColumnRealType("Double");
            }else if(column.getColumnType().startsWith(FieldForGeneratorMysql.DATE)){
                column.setColumnRealType("Date");
            }
            column.setGetterMethodName(_BeanUtil.getGetterMethodName(column.getColumnRealName()));
            column.setSetterMethodName(_BeanUtil.getSetterMethodName(column.getColumnRealName()));
            column.setIsPrimaryKey("PRI".equalsIgnoreCase(column.getColumnKey()));
        }
        return list;
    }


    private static String author;
    private static String nowDateTime;
    private static String moduleName;
    private static String tableName;
    private static String tableComment;
    private static String javabeanName;
    private static String javabeanVar;
    private static List<FieldForGeneratorMysql> fieldList;
    /** 包的根路径 **/
    private static String basicPackageName;
    /** 存放flt生成模板文件的目录路径 **/
    private static String templateFoldPath;


}
